package com.corpgovernment.organization.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.corpgovernment.common.enums.ResourceEnum;
import com.corpgovernment.costcenter.entity.db.MbOrgCostDepartment;
import com.corpgovernment.costcenter.entity.db.MbOrgProjectDepartment;
import com.corpgovernment.costcenter.mapper.MbOrgCostDepartmentMapper;
import com.corpgovernment.costcenter.mapper.MbOrgProjectInfoMapper;
import com.corpgovernment.organization.dto.AsyncDepartmentCompanyDTO;
import com.corpgovernment.organization.entity.db.MbBookingRelation;
import com.corpgovernment.organization.entity.db.MbOrgInfo;
import com.corpgovernment.organization.enumm.MbRoleResourceTypeEnum;
import com.corpgovernment.organization.enumm.OrgTypeEnum;
import com.corpgovernment.organization.mapper.BookingRelationMapper;
import com.corpgovernment.organization.mapper.MbOrgInfoMapper;
import com.corpgovernment.permission.entity.db.MbRoleResource;
import com.corpgovernment.permission.mapper.MbRoleResourceMapper;
import com.corpgovernment.permission.service.RoleResourceService;
import com.ctrip.corp.obt.generic.utils.JsonUtils;
import com.ctrip.corp.obt.generic.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author zhengshi.wang
 * @description:异步处理新增部门或者公司模版方法类
 * @date 2024/4/2
 */
@Slf4j
@Component
public class AsyncDepartmentCompanyService extends AbstractAsyncDepartmentCompany {
    @Autowired
    private MbOrgInfoMapper mbOrgInfoMapper;
    @Autowired
    private MbRoleResourceMapper mbRoleResourceMapper;
    @Autowired
    private BookingRelationMapper bookingRelationMapper;
    @Autowired
    private MbOrgCostDepartmentMapper mbOrgCostDepartmentMapper;
    @Autowired
    private MbOrgProjectInfoMapper mbOrgProjectInfoMapper;
    @Autowired
    private RoleResourceService roleResourceService;

    @Override
    void buildDTO(AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO) {
        log.info("异步处理新增部门或者公司模版方法类,handle,asyncDepartmentCompanyDTO,start:{}", JsonUtils.toJsonString(asyncDepartmentCompanyDTO));

        String parentOrgId = asyncDepartmentCompanyDTO.getParentOrgId();
        if(StringUtils.isBlank(parentOrgId)){
            log.info("没有parentOrgId,asyncDepartmentCompanyDTO:{}", JsonUtils.toJsonString(asyncDepartmentCompanyDTO));
            return;
        }
        List<MbOrgInfo> parentMbOrgInfo= mbOrgInfoMapper.selectByOrgIds(Arrays.asList(parentOrgId));
        log.info("异步处理新增部门或者公司模版方法类,parentMbOrgInfo,parentOrgId:{},parentMbOrgInfo:{}",
                parentOrgId,JsonUtils.toJsonString(parentMbOrgInfo));
        if(CollectionUtil.isEmpty(parentMbOrgInfo)){
            log.info("没有parentMbOrgInfo:{}", JsonUtils.toJsonString(parentMbOrgInfo));
            return;
        }
        asyncDepartmentCompanyDTO.setParentId(parentMbOrgInfo.get(0).getId().toString());

        List<MbOrgInfo> childrenMbOrgInfoPenetrationList = getChildrenMbOrgInfoPenetrationList(parentOrgId, asyncDepartmentCompanyDTO);
        log.info("异步处理新增部门或者公司模版方法类,childrenMbOrgInfoPenetrationList,parentOrgId:{},childrenMbOrgInfoPenetrationList:{}",
                JsonUtils.toJsonString(parentOrgId),JsonUtils.toJsonString(childrenMbOrgInfoPenetrationList));
        // 穿透子公司(包括子公司)
        setChildIdPenetrationList(asyncDepartmentCompanyDTO, childrenMbOrgInfoPenetrationList);

        List<MbOrgInfo> childrenMbOrgInfoList = getChildrenMbOrgInfoList(parentOrgId, asyncDepartmentCompanyDTO);
        log.info("异步处理新增部门或者公司模版方法类,getChildrenMbOrgInfoList:{}",JsonUtils.toJsonString(childrenMbOrgInfoList));
        // 不穿透子公司(包括子公司)
        setChildIdList(asyncDepartmentCompanyDTO, childrenMbOrgInfoList);

        log.info("异步处理新增部门或者公司模版方法类,构建DTO,asyncDepartmentCompanyDTO,end:{}", JsonUtils.toJsonString(asyncDepartmentCompanyDTO));
    }

    /**
     * 穿透子公司(包括子公司)获取子组织信息列表，并过滤掉新添加的
     *
     * @param parentOrgId 父组织ID
     * @param asyncDepartmentCompanyDTO DTO对象
     * @return 子组织信息列表
     */
    private List<MbOrgInfo> getChildrenMbOrgInfoPenetrationList(String parentOrgId, AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO) {
        List<MbOrgInfo> childrenMbOrgInfoList = mbOrgInfoMapper.selectAllChildrenOrgIdsPenetration(parentOrgId);
        if (CollectionUtil.isNotEmpty(childrenMbOrgInfoList)) {
            childrenMbOrgInfoList = childrenMbOrgInfoList.stream()
                    .filter(m -> !String.valueOf(m.getId()).equals(asyncDepartmentCompanyDTO.getNewId()))
                    .collect(Collectors.toList());
        }
        return childrenMbOrgInfoList;
    }

    /**
     * 不穿透子公司(不包括子公司)获取子组织信息列表，并过滤掉新添加的
     * @param parentOrgId
     * @param asyncDepartmentCompanyDTO
     * @return
     */
    private List<MbOrgInfo> getChildrenMbOrgInfoList(String parentOrgId, AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO) {
        List<MbOrgInfo> childrenMbOrgInfoList = mbOrgInfoMapper.selectAllChildrenOrgIds(parentOrgId);
        if (CollectionUtil.isNotEmpty(childrenMbOrgInfoList)) {
            childrenMbOrgInfoList = childrenMbOrgInfoList.stream()
                    .filter(m -> !String.valueOf(m.getId()).equals(asyncDepartmentCompanyDTO.getNewId()))
                    .collect(Collectors.toList());
        }
        return childrenMbOrgInfoList;
    }

    /**
     * 设置DTO对象的childIdList和childOrgIdList属性
     *
     * @param asyncDepartmentCompanyDTO DTO对象
     * @param childrenMbOrgInfoList 子组织信息列表
     */
    private void setChildIdPenetrationList(AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO, List<MbOrgInfo> childrenMbOrgInfoList) {
        List<String> childIdList = childrenMbOrgInfoList.stream()
                .map(MbOrgInfo::getId)
                .map(String::valueOf)
                .collect(Collectors.toList());
        asyncDepartmentCompanyDTO.setChildIdPenetrationList(childIdList);

        List<String> childOrgIdList = childrenMbOrgInfoList.stream()
                .map(MbOrgInfo::getOrgId)
                .collect(Collectors.toList());
        asyncDepartmentCompanyDTO.setChildOrgIdPenetrationList(childOrgIdList);
    }

    private void setChildIdList(AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO, List<MbOrgInfo> childrenMbOrgInfoList) {
        List<String> childIdList = childrenMbOrgInfoList.stream()
                .map(MbOrgInfo::getId)
                .map(String::valueOf)
                .collect(Collectors.toList());
        asyncDepartmentCompanyDTO.setChildIdList(childIdList);

        List<String> childOrgIdList = childrenMbOrgInfoList.stream()
                .map(MbOrgInfo::getOrgId)
                .collect(Collectors.toList());
        asyncDepartmentCompanyDTO.setChildOrgIdList(childOrgIdList);
    }

    @Override
    void handleDepartment(AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO) {
        List<String> roleIdList = getRoleIdDepartment(asyncDepartmentCompanyDTO);
        log.info("handleDepartment,部门权限,roleIdList:{}", JsonUtils.toJsonString(roleIdList));
        if(CollectionUtil.isNotEmpty(roleIdList)){
            addRoleResource(roleIdList,asyncDepartmentCompanyDTO.getNewId(),Integer.valueOf(MbRoleResourceTypeEnum.DEPARTMENT.getCode()));
            // 更新redis缓存
            updateRedisCache(roleIdList,asyncDepartmentCompanyDTO);
        }
    }
    @Override
    void handleBilling(AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO) {
        List<String> roleIdList = getRoleIdBilling(asyncDepartmentCompanyDTO);
        log.info("handleBilling,开票权限,roleIdList:{}", JsonUtils.toJsonString(roleIdList));
        if(CollectionUtil.isNotEmpty(roleIdList)){
            addRoleResource(roleIdList,asyncDepartmentCompanyDTO.getNewId(),Integer.valueOf(MbRoleResourceTypeEnum.BILLING.getCode()));
            // 更新redis缓存
            updateRedisCache(roleIdList,asyncDepartmentCompanyDTO);
        }
    }

    @Override
    void handleBookingAgent(AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO) {
        log.info("handleBookingAgent,asyncDepartmentCompanyDTO:{}",JsonUtils.toJsonString(asyncDepartmentCompanyDTO));
        // 如果是叶子结点添加子节点,就把原来有叶子节点权限的人都赋予权限
        if(CollectionUtil.isEmpty(asyncDepartmentCompanyDTO.getChildOrgIdList()) &&
                CollectionUtil.isEmpty(asyncDepartmentCompanyDTO.getChildOrgIdPenetrationList())){
            //查询所有预订了父节点的uid
            List<String>  uidList = bookingRelationMapper.distinctUid(asyncDepartmentCompanyDTO.getParentOrgId());
            addRoleBookingAgentResource(uidList,asyncDepartmentCompanyDTO.getNewOrgId());
            return;
        }
        List<String> uidList = getUidBookingAgent(asyncDepartmentCompanyDTO);
        log.info("handleBookingAgent,uidList:{}", JsonUtils.toJsonString(uidList));
        if(CollectionUtil.isNotEmpty(uidList)){
            addRoleBookingAgentResource(uidList,asyncDepartmentCompanyDTO.getNewOrgId());
        }
    }

    @Override
    void handleCostCenter(AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO) {
        // 不穿透公司,新增公司类型时就返回
        if(asyncDepartmentCompanyDTO.getType().equalsIgnoreCase(OrgTypeEnum.C.getCode())){
            log.info("handleCostCenter,return,type:{}", asyncDepartmentCompanyDTO.getType());
            return;
        }
        List<String> costInfoIdList = getCostInfoId(asyncDepartmentCompanyDTO);
        log.info("handleCostCenter,costInfoIdList:{}", JsonUtils.toJsonString(costInfoIdList));
        if(CollectionUtil.isNotEmpty(costInfoIdList)){
            addCostCenterResource(costInfoIdList,asyncDepartmentCompanyDTO.getNewOrgId(),asyncDepartmentCompanyDTO.getNewOrgName());
        }
    }

    @Override
    void handleProject(AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO) {
        // 不穿透公司,新增公司类型时就返回
        if(asyncDepartmentCompanyDTO.getType().equalsIgnoreCase(OrgTypeEnum.C.getCode())){
            log.info("handleCostCenter,return,type:{}", asyncDepartmentCompanyDTO.getType());
            return;
        }
        List<String> projectIdList = getProjectId(asyncDepartmentCompanyDTO);
        log.info("handleProject,projectIdList:{}", JsonUtils.toJsonString(projectIdList));
        if(CollectionUtil.isNotEmpty(projectIdList)){
            addProjectResource(projectIdList,asyncDepartmentCompanyDTO.getNewOrgId(),asyncDepartmentCompanyDTO.getNewOrgName());
        }
    }

    List<String> getRoleIdDepartment(AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO) {
        return mbOrgInfoMapper.selectRoleId(asyncDepartmentCompanyDTO.getParentId(),asyncDepartmentCompanyDTO.getChildIdPenetrationList(),
                asyncDepartmentCompanyDTO.getChildIdPenetrationList().size(), MbRoleResourceTypeEnum.DEPARTMENT.getCode());
    }

    /**
     * 添加角色的资源
     * @param roleIdList 角色列表
     * @param id 资源id
     */
    void addRoleResource(List<String> roleIdList, String id,Integer type) {
        List<MbRoleResource> list = convertRoleIdListToMbRoleResourceList(roleIdList, id , type);
        log.info("部门权限,addRoleDepartment,list:{}", JsonUtils.toJsonString(list));
        mbRoleResourceMapper.batchInsert(list);
    }

    private List<MbRoleResource> convertRoleIdListToMbRoleResourceList(List<String> roleIdList, String id,Integer type) {
        return roleIdList.stream()
                .map(roleId -> createMbRoleResource(roleId, id,type))
                .collect(Collectors.toList());
    }

    private MbRoleResource createMbRoleResource(String roleId, String id,Integer type) {
        MbRoleResource mbRoleResource = new MbRoleResource();
        mbRoleResource.setRoleId(Long.valueOf(roleId));
        mbRoleResource.setResourceId(Long.valueOf(id));
        mbRoleResource.setType(type);
        mbRoleResource.setDeleteTime(new Date(0)); // 1970-01-01 00:00:00 GMT
        mbRoleResource.setDatachangeCreatetime(new Date());
        mbRoleResource.setDatachangeLasttime(new Date());
        return mbRoleResource;
    }

    List<String> getRoleIdBilling(AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO) {
        return mbOrgInfoMapper.selectRoleId(asyncDepartmentCompanyDTO.getParentId(),asyncDepartmentCompanyDTO.getChildIdPenetrationList(),
                asyncDepartmentCompanyDTO.getChildIdPenetrationList().size(),MbRoleResourceTypeEnum.BILLING.getCode());
    }

    List<String> getUidBookingAgent(AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO) {
        return bookingRelationMapper.selectUidBookingAgent(asyncDepartmentCompanyDTO.getParentOrgId(),
                asyncDepartmentCompanyDTO.getChildOrgIdPenetrationList(),asyncDepartmentCompanyDTO.getChildOrgIdPenetrationList().size());
    }

    List<String> getCostInfoId(AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO) {
        return mbOrgCostDepartmentMapper.selectCostInfoIdCostCenter(asyncDepartmentCompanyDTO.getParentOrgId(),
                asyncDepartmentCompanyDTO.getChildOrgIdList(),
                asyncDepartmentCompanyDTO.getChildOrgIdList().size());
    }

    List<String> getProjectId(AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO) {
        return mbOrgProjectInfoMapper.selectProjectId(asyncDepartmentCompanyDTO.getParentOrgId(),
                asyncDepartmentCompanyDTO.getChildOrgIdList(),
                asyncDepartmentCompanyDTO.getChildIdList().size());
    }

    @Override
    void updateRedisCache(List<String> roleIdList,AsyncDepartmentCompanyDTO asyncDepartmentCompanyDTO) {
        log.info("updateRedisCache,roleIdList:{},asyncDepartmentCompanyDTO:{}",JsonUtils.toJsonString(roleIdList)
                ,JsonUtils.toJsonString(asyncDepartmentCompanyDTO));
        for (String roleId : roleIdList) {
            roleResourceService.addOneResource(Long.valueOf(roleId),
                    Long.valueOf(asyncDepartmentCompanyDTO.getNewId()),
                    asyncDepartmentCompanyDTO.getNewOrgId(),
                    asyncDepartmentCompanyDTO.getType(),
                    ResourceEnum.DEPT.getCode());
        }
    }

    void addRoleBookingAgentResource(List<String> uidList, String orgId) {
        if(CollectionUtil.isEmpty(uidList) || StrUtil.isEmpty(orgId)){
            log.info("addRoleBookingAgent,empty,uidList:{},orgId:{}",JsonUtils.toJsonString(uidList),orgId);
            return;
        }
        List<MbBookingRelation>  mbBookingRelationList = bookingRelationMapper.selectAllByUid(uidList);
        if(CollectionUtil.isEmpty(mbBookingRelationList)){
            log.info("addRoleBookingAgent-mbBookingRelationList,empty,uidList:{},orgId:{}",JsonUtils.toJsonString(uidList),orgId);
            return;
        }
        Map<String,MbBookingRelation> map =
                mbBookingRelationList.stream().collect(Collectors.toMap(MbBookingRelation::getUid, Function.identity(), (oldValue, newValue) -> oldValue));
        List<MbBookingRelation> list = uidList.stream()
                .map(uid -> createMbBookingRelation(uid, orgId, map))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toList());
        log.info("addRoleBookingAgent,list:{}",JsonUtils.toJsonString(list));
        bookingRelationMapper.batchInsert(list);
    }

    private Optional<MbBookingRelation> createMbBookingRelation(String uid, String orgId, Map<String, MbBookingRelation> map) {
        MbBookingRelation existingRelation = map.get(uid);
        if (ObjectUtil.isEmpty(existingRelation)) {
            return Optional.empty();
        }
        MbBookingRelation mbBookingRelation = new MbBookingRelation();
        mbBookingRelation.setUid(uid);
        mbBookingRelation.setOrgId(existingRelation.getOrgId());
        mbBookingRelation.setBookOrgId(orgId);
        mbBookingRelation.setStatus("T");
        mbBookingRelation.setBookForNonEmployee(existingRelation.getBookForNonEmployee());
        mbBookingRelation.setDatachangeCreatetime(new Date());
        mbBookingRelation.setDatachangeLasttime(new Date());
        return Optional.of(mbBookingRelation);
    }

    void addCostCenterResource(List<String> costInfoIdList, String orgId, String orgName) {
        if(CollectionUtil.isEmpty(costInfoIdList) || StrUtil.isEmpty(orgId) || StrUtil.isEmpty(orgName)){
            log.info("addRoleCostCenter,empty,costInfoIdList:{},orgId:{},orgName:{}",
                    JsonUtils.toJsonString(costInfoIdList),orgId,orgName);
            return;
        }
        List<MbOrgCostDepartment> list = costInfoIdList.stream()
                .map(costInfoId -> createMbOrgCostDepartment(costInfoId, orgId, orgName))
                .collect(Collectors.toList());
        log.info("mbOrgCostDepartmentMapper.batchInsert,list:{}",JsonUtils.toJsonString(list));
        mbOrgCostDepartmentMapper.batchInsert(list);
    }

    private MbOrgCostDepartment createMbOrgCostDepartment(String costInfoId, String orgId, String orgName) {
        MbOrgCostDepartment mbOrgCostDepartment = new MbOrgCostDepartment();
        mbOrgCostDepartment.setOrgName(orgName);
        mbOrgCostDepartment.setCostInfoId(Integer.valueOf(costInfoId));
        mbOrgCostDepartment.setOrgId(orgId);
        mbOrgCostDepartment.setCreateTime(new Date());
        mbOrgCostDepartment.setUpdateTime(new Date());
        return mbOrgCostDepartment;
    }

    void addProjectResource(List<String> projectIdList, String orgId, String orgName) {
        if(CollectionUtil.isEmpty(projectIdList) || StrUtil.isEmpty(orgId) || StrUtil.isEmpty(orgName)){
            log.info("addRoleCostCenter,empty,projectIdList:{},orgId:{},orgName:{}",
                    JsonUtils.toJsonString(projectIdList),orgId,orgName);
            return;
        }
        List<MbOrgProjectDepartment> list = projectIdList.stream()
                .map(projectId -> createMbOrgProjectDepartment(projectId, orgId, orgName))
                .collect(Collectors.toList());
        log.info("addProjectResource.batchInsert,list:{}",JsonUtils.toJsonString(list));
        mbOrgProjectInfoMapper.batchInsert(list);
    }

    private MbOrgProjectDepartment createMbOrgProjectDepartment(String projectId, String orgId, String orgName) {
        MbOrgProjectDepartment mbOrgProjectInfo = new MbOrgProjectDepartment();
        mbOrgProjectInfo.setProjectId(Integer.valueOf(projectId));
        mbOrgProjectInfo.setOrgId(orgId);
        mbOrgProjectInfo.setOrgName(orgName);
        mbOrgProjectInfo.setCreateTime(new Date());
        mbOrgProjectInfo.setUpdateTime(new Date());
        return mbOrgProjectInfo;
    }
}
